Added a hosts-allow facility to TCP connections, which allows us to restrict
authoremellor@leeni.uk.xensource.com <emellor@leeni.uk.xensource.com>
Mon, 12 Dec 2005 16:43:48 +0000 (16:43 +0000)
committeremellor@leeni.uk.xensource.com <emellor@leeni.uk.xensource.com>
Mon, 12 Dec 2005 16:43:48 +0000 (16:43 +0000)
the acceptable connections based upon a regular expression comparison with the
FQDN or the IP address.

Use the hosts-allow facility to restrict access to the relocation socket.  This
adds the configuration option xend-relocation-hosts-allow, which takes a
space-separated sequence of regular expressions.

Pass the protocol class instance through to SocketServerConnection, rather than
a new instance of that class.  This means that the new instance need not be
passed through SocketListener.acceptConnection.

Make the SocketServerConnection and SocketListener classes start their
corresponding threads and open their sockets (in the case of SocketListener)
automatically.  This means that callers do not need to save an instance locally,
just to call run() or listen() on it.  This also means that listenTCP and
listenUnix can go -- simply creating a TCPListener or UnixListener instance is
sufficient.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
tools/python/xen/web/connection.py
tools/python/xen/web/tcp.py
tools/python/xen/web/unix.py
tools/python/xen/xend/XendRoot.py
tools/python/xen/xend/server/relocate.py

index 342e6a6f5979c5f67b3920c3a1ea73280a08497b..cc3660d5387679c50b5bb462feaaf9841338a0fa 100644 (file)
@@ -19,7 +19,6 @@
 
 import sys
 import threading
-import select
 import socket
 import fcntl
 
@@ -31,21 +30,17 @@ for TCP and unix-domain sockets (see tcp.py and unix.py).
 """
 
 BUFFER_SIZE = 1024
+BACKLOG = 5
 
 
 class SocketServerConnection:
     """An accepted connection to a server.
     """
 
-    def __init__(self, sock, protocol, addr, server):
+    def __init__(self, sock, protocol_class):
         self.sock = sock
-        self.protocol = protocol
-        self.addr = addr
-        self.server = server
+        self.protocol = protocol_class()
         self.protocol.setTransport(self)
-
-
-    def run(self):
         threading.Thread(target=self.main).start()
 
 
@@ -68,6 +63,10 @@ class SocketServerConnection:
                 pass
 
 
+    def close(self):
+        self.sock.close()
+
+
     def write(self, data):
         self.sock.send(data)
 
@@ -77,52 +76,38 @@ class SocketListener:
     Accepts connections and runs a thread for each one.
     """
 
-    def __init__(self, protocol_class, backlog=None):
-        if backlog is None:
-            backlog = 5
+    def __init__(self, protocol_class, hosts_allow = ''):
         self.protocol_class = protocol_class
-        self.sock = None
-        self.backlog = backlog
-        self.thread = None
+        self.sock = self.createSocket()
+        threading.Thread(target=self.main).start()
 
 
-    def createSocket(self):
-        raise NotImplementedError()
+    def close(self):
+        try:
+            self.sock.close()
+        except:
+            pass
 
 
-    def setCloExec(self):
-        fcntl.fcntl(self.sock.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
+    def createSocket(self):
+        raise NotImplementedError()
 
 
     def acceptConnection(self, sock, protocol, addr):
-        return SocketServerConnection(sock, protocol, addr, self)
-
-
-    def listen(self):
-        if self.sock or self.thread:
-            raise IOError("already listening")
-        self.sock = self.createSocket()
-        self.sock.listen(self.backlog)
-        self.run()
-
-
-    def run(self):
-        self.thread = threading.Thread(target=self.main)
-        self.thread.start()
+        raise NotImplementedError()
 
 
     def main(self):
         try:
+            fcntl.fcntl(self.sock.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
+            self.sock.listen(BACKLOG)
+
             while True:
                 try:
                     (sock, addr) = self.sock.accept()
-                    self.acceptConnection(sock, self.protocol_class(),
-                                          addr).run()
+                    self.acceptConnection(sock, addr)
                 except socket.error, ex:
                     if ex.args[0] not in (EWOULDBLOCK, EAGAIN, EINTR):
                         break
         finally:
-            try:
-                self.sock.close()
-            except:
-                pass
+            self.close()
index f3d67a7de4d4c0c9cc3416453a45b9ef1a31b4da..552bf4a5cc9d733db20ae4e2a2d61fadc540c05f 100644 (file)
 #============================================================================
 
 
+import errno
+import re
 import socket
 import time
-import errno
 
-from connection import *
+import connection
+
+from xen.xend.XendLogging import log
 
 
-class TCPListener(SocketListener):
+class TCPListener(connection.SocketListener):
 
-    def __init__(self, port, protocol, backlog=None, interface=''):
-        SocketListener.__init__(self, protocol, backlog=backlog)
+    def __init__(self, protocol_class, port, interface, hosts_allow):
         self.port = port
         self.interface = interface
-        
+        self.hosts_allow = hosts_allow
+        connection.SocketListener.__init__(self, protocol_class)
+
+
     def createSocket(self):
         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
@@ -49,11 +54,23 @@ class TCPListener(SocketListener):
                 else:
                     raise
 
-    def acceptConnection(self, sock, protocol, addr):
-        return SocketServerConnection(sock, protocol, addr, self)
 
+    def acceptConnection(self, sock, addrport):
+        addr = addrport[0]
+        if self.hosts_allow is None:
+                connection.SocketServerConnection(sock, self.protocol_class)
+        else:
+            fqdn = socket.getfqdn(addr)
+            for h in self.hosts_allow:
+                if h.match(fqdn) or h.match(addr):
+                    log.debug("Match %s %s", fqdn, h.pattern)
+                    connection.SocketServerConnection(sock,
+                                                      self.protocol_class)
+                    return
 
-def listenTCP(port, protocol, interface='', backlog=None):
-    l = TCPListener(port, protocol, interface=interface, backlog=backlog)
-    l.listen()
-    l.setCloExec()
+            try:
+                log.warn("Rejected connection from %s:%d (%s) for port %d.",
+                         addr, addrport[1], fqdn, self.port)
+                sock.close()
+            except:
+                pass
index 64ae2fcf1bf2b973d20faa7621da9c0f2e9ffae4..174e7284bc6a80762bcddeedbd1bf9819b063d02 100644 (file)
@@ -21,15 +21,15 @@ import socket
 import os
 import os.path
 
-from connection import *
+import connection
 
 
-class UnixListener(SocketListener):
-
-    def __init__(self, path, protocol, backlog=None):
-        SocketListener.__init__(self, protocol, backlog=backlog)
+class UnixListener(connection.SocketListener):
+    def __init__(self, path, protocol_class):
         self.path = path
-        
+        connection.SocketListener.__init__(self, protocol_class)
+
+
     def createSocket(self):
         pathdir = os.path.dirname(self.path)
         if not os.path.exists(pathdir):
@@ -45,9 +45,6 @@ class UnixListener(SocketListener):
         sock.bind(self.path)
         return sock
 
-    def acceptConnection(self, sock, protocol, addr):
-        return SocketServerConnection(sock, protocol, self.path, self)
-
 
-def listenUNIX(path, protocol, backlog=None):
-    UnixListener(path, protocol, backlog=backlog).listen()
+    def acceptConnection(self, sock, _):
+        connection.SocketServerConnection(sock, self.protocol_class)
index b61532737a6af3c3a196d7561ec9a7e39ff8262f..c806f75e2f29d695a3182ea98efcd761a8caa398 100644 (file)
@@ -75,6 +75,8 @@ class XendRoot:
     """Default port xend serves relocation at. """
     xend_relocation_port_default = '8002'
 
+    xend_relocation_hosts_allow_default = ''
+
     """Default for the flag indicating whether xend should run a unix-domain server."""
     xend_unix_server_default = 'yes'
 
@@ -194,6 +196,10 @@ class XendRoot:
         """
         return self.get_config_int('xend-relocation-port', self.xend_relocation_port_default)
 
+    def get_xend_relocation_hosts_allow(self):
+        return self.get_config_value("xend-relocation-hosts-allow",
+                                     self.xend_relocation_hosts_allow_default)
+
     def get_xend_address(self):
         """Get the address xend listens at for its HTTP port.
         This defaults to the empty string which allows all hosts to connect.
index c20d57715625d96952533475fc6245ac5dcad2ac..1b7ed157e9fa4c309ced3e0011bcf21a2b0a7ae0 100644 (file)
@@ -16,6 +16,7 @@
 # Copyright (C) 2005 XenSource Ltd
 #============================================================================
 
+import re
 import sys
 import StringIO
 
@@ -116,8 +117,16 @@ def listenRelocation():
     xroot = XendRoot.instance()
     if xroot.get_xend_unix_server():
         path = '/var/lib/xend/relocation-socket'
-        unix.listenUNIX(path, RelocationProtocol)
+        unix.UnixListener(path, RelocationProtocol)
     if xroot.get_xend_relocation_server():
         port = xroot.get_xend_relocation_port()
         interface = xroot.get_xend_relocation_address()
-        tcp.listenTCP(port, RelocationProtocol, interface=interface)
+
+        hosts_allow = xroot.get_xend_relocation_hosts_allow()
+        if hosts_allow == '':
+            hosts_allow = None
+        else:
+            hosts_allow = map(re.compile, hosts_allow.split(" "))
+
+        tcp.TCPListener(RelocationProtocol, port, interface = interface,
+                        hosts_allow = hosts_allow)